﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Tools.MultipleIdGenerator
{
    /// <summary>
    /// 生成数字编号的类，线程安全的，默认的实现则需要一个文件写入的权限；2010-08-06增加该类
    /// </summary>
    public class Generator
    {
        private static Dictionary<string, IdGenerator> _dict = new Dictionary<string, IdGenerator>();
        private static Dictionary<string, IDataStore> _dataStore = new Dictionary<string, IDataStore>();

        #region 生成一个 IdGenerator

        /// <summary>
        /// key和dataStore不能为null，bufferSize和increment可以为0
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataPath"></param>
        /// <param name="bufferSize"></param>
        /// <param name="increment"></param>
        /// <returns></returns>
        static IdGenerator GetGenerator(string key, string dataPath, int bufferSize, int increment)
        {
            IDataStore st = null;
            //先产生DataStore
            if (!_dataStore.ContainsKey(key))
            {
                lock (_dataStore)
                {
                    st = new FileDataStore(dataPath);
                    if (!_dataStore.ContainsKey(dataPath))
                        _dataStore.Add(dataPath, st);
                }
            }
            else
                st = _dataStore[dataPath];

            return GetGenerator(key, st, bufferSize, increment);
        }

        static IdGenerator GetGenerator(string key, IDataStore dataStore, int bufferSize, int increment)
        {
            if (!_dict.ContainsKey(key))
            {
                lock (_dict)//防止多线程调用出现“已添加了具有相同键的项”
                {
                    if (increment == 0)//2011-02-23 增加 by 杜有发
                        increment = IdGenerator.DEFAULT_INCREMENT;

                    //Console.WriteLine(increment);

                    IdGenerator g;
                    if (bufferSize != 0)
                        g = new IdGenerator(key, bufferSize, increment);
                    else
                        g = new IdGenerator(key, IdGenerator.DEFAULT_BUFFER_SIZE, increment);

                    g.DataStore = dataStore;

                    if (!_dict.ContainsKey(key)) //db check，前面的“if (!_dict.ContainsKey(key))”不是线程安全的
                        _dict.Add(key, g);
                    else
                        g = null;
                }
            }

            return _dict[key];
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataPath"></param>
        /// <param name="bufferSize"></param>
        /// <returns></returns>
        public static long Next(string key, string dataPath, int bufferSize)
        {
            return GetGenerator(key, dataPath, bufferSize, 0).Next();
        }

        /// <summary>
        /// key和dataStore不能为null，bufferSize和increment可以为0
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataStore"></param>
        /// <param name="bufferSize"></param>
        /// <param name="increment"></param>
        /// <returns></returns>
        public static long Next(string key, IDataStore dataStore, int bufferSize, int increment)
        {
            return GetGenerator(key, dataStore, bufferSize, increment).Next();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataStore"></param>
        /// <param name="bufferSize"></param>
        /// <returns></returns>
        public static long Next(string key, IDataStore dataStore, int bufferSize)
        {
            return Next(key, dataStore, bufferSize, 0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataPath"></param>
        /// <returns></returns>
        public static long Next(string key, string dataPath)
        {
            return Next(key, dataPath, 0);
        }

        /// <summary>
        /// key和dataStore不能为null
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataStore"></param>
        /// <returns></returns>
        public static long Next(string key, IDataStore dataStore)
        {
            return Next(key, dataStore, 0);
        }

        /// <summary>
        /// 设置初始值，生成的最小值从初始值+1开始
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataPath"></param>
        /// <param name="value"></param>
        /// <param name="increment"></param>     
        public static void SetInitialValue(string key, string dataPath, long value, int increment)
        {
            GetGenerator(key, dataPath, 0, increment).SetInitialValue(value);
        }

        /// <summary>
        /// 设置初始值，生成的最小值从初始值+1开始
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataStore"></param>
        /// <param name="value"></param>  
        /// <param name="increment"></param>
        public static void SetInitialValue(string key, IDataStore dataStore, long value, int increment)
        {
            GetGenerator(key, dataStore, 0, increment).SetInitialValue(value);
        }
    }

    /// <summary>
    /// key-value存储器的接口，不用考虑线程安全 2011-07-21
    /// </summary>
    public interface IDataStore
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        long Get(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        void Set(string key, long value);
    }

    internal class FileDataStore : IDataStore
    {
        private string _baseDirectory;

        /// <summary>
        /// 
        /// </summary>
        public string BaseDirectory
        {
            get { return _baseDirectory; }
            set { _baseDirectory = value; }
        }

        public FileDataStore(string baseDirectory)
        {
            _baseDirectory = baseDirectory;

            if (!string.IsNullOrEmpty(baseDirectory))
            {
                _baseDirectory = IO.ToAbsolutePath(baseDirectory);//2010-08-10修改为ToAbsolutePath
                if (_baseDirectory.EndsWith("/") || _baseDirectory.EndsWith("\\"))//确保默认有\号
                { }
                else
                    _baseDirectory += "\\";

                IO.MakeSureDirectoryPathExists(_baseDirectory);//2010-08-10增加，确保文件夹路径是存在的
            }
            else
                _baseDirectory = AppDomain.CurrentDomain.BaseDirectory;//末尾存在\号 
        }

        public void Set(string key, long value)
        {
            string filePath = GetFilePath(key);
            IO.WriteStringToFile(value.ToString(), filePath, Encoding.UTF8);
        }

        string GetFilePath(string key)
        {
            return _baseDirectory + key + ".txt";
        }

        public long Get(string key)
        {
            string filePath = GetFilePath(key);
            string temp = IO.ReadStringFromFile(filePath, Encoding.UTF8);

            long nowId = 0;
            if (!temp.Equals(""))
                nowId = Convert.StrToLong(temp, 0);

            return nowId;
        }
    }

    internal class IdGenerator
    {
        private object _obj = new object();
        private string _key;
        private long _nowId;
        private bool _isInitialized = false;
        private int _increment = 1;//2011-02-23 增加 by 杜有发

        /*/// <summary>
        /// 版本2加入一个缓冲队列
        /// </summary>
        private Queue<int> _queue = new Queue<int>();*/

        /// <summary>
        /// 默认缓冲器大小
        /// </summary>
        public const int DEFAULT_BUFFER_SIZE = 20;

        /// <summary>
        /// 默认的增量
        /// </summary>
        public const int DEFAULT_INCREMENT = 1;

        /// <summary>
        /// 2010-08-11增加
        /// </summary>
        int _bufferSize = DEFAULT_BUFFER_SIZE;

        /// <summary>
        /// 方案3加入，这里不再创建缓冲区，而是直接加1返回id；如果_left=0，再重新申请一批
        /// </summary>
        private int _left = 0;

        /// <summary>
        /// 数据存储 2010-12-23
        /// </summary>
        private IDataStore _dataStore;

        public IDataStore DataStore
        {
            get { return _dataStore; }
            set { _dataStore = value; }
        }

        /// <summary>
        /// 使用程序当前目录作为数据存储目录
        /// </summary>
        /// <param key="key"></param>
        public IdGenerator(string key)
            : this(key, DEFAULT_BUFFER_SIZE)
        {
        }

        /// <summary>
        /// 直接new一个，一些属性在外面进行设置
        /// </summary>
        public IdGenerator()
            : this(null, DEFAULT_BUFFER_SIZE)
        { }

        /// <summary>
        /// 数据文件存储的目录
        /// </summary>
        /// <param key="key"></param>
        /// <param key="bufferSize"></param>
        public IdGenerator(string key, int bufferSize)
            : this(key, bufferSize, 1)
        { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="bufferSize"></param>
        /// <param name="increment"></param>
        public IdGenerator(string key, int bufferSize, int increment)
        {
            _key = key;
            _bufferSize = bufferSize;
            _increment = increment;//2011-02-23
        }

        /// <summary>
        /// 请在Init()方法之前调用,设置初始值,慎用
        /// </summary>
        /// <param key="value"></param>
        public void SetInitialValue(long value)
        {
            CheckProperties();

            lock (_obj)
            {
                //_isInitialized = false;

                //2010-10-22 增加 by 杜有发
                _nowId = value;
                _left = 0;

                _dataStore.Set(_key, value);
            }
        }

        /// <summary>
        /// 2010-08-11增加
        /// </summary>
        /// <param key="value"></param>
        public void SetBufferSize(int value)
        {
            if (_isInitialized)
                throw new Exception("已经初始化了，设置BufferSize无效");

            _bufferSize = value;
        }

        /// <summary>
        /// 检查key,bufferSize属性 2010-08-05增加
        /// </summary>
        void CheckProperties()
        {
            if (string.IsNullOrEmpty(_key))
                throw new Exception("key属性不能为空");
            if (!IO.IsValidFileName(_key))
                throw new Exception("key不合法，不能包含特殊字符");

            if (_bufferSize < 1)
                throw new Exception("bufferSize值(" + _bufferSize + ")不合法");

            //判断增量信息
            if (_increment == 0)
                throw new Exception("increment不能等于0");

            if (_dataStore == null)
                throw new Exception("请设置DataStore属性");
        }

        /// <summary>
        /// 初始化，可以单独调用
        /// </summary>
        public void Init()
        {
            if (_isInitialized)//如果初始化了，则返回
                return;

            CheckProperties();

            lock (_obj)
            {
                if (_isInitialized)//double check
                    return;

                _nowId = _dataStore.Get(_key);

                _isInitialized = true;

                _left = 0;//2010-08-04增加，剩余的编号
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Name
        {
            get { return _key; }
            set
            {
                if (!string.IsNullOrEmpty(_key))//原来key属性有值
                {
                    if (!_key.Equals(value))//key属性发生了变化，要重新初始化
                    {
                        _key = value;
                        _isInitialized = false;//标志位
                    }
                }
                else
                    _key = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsInitialized
        {
            get { return _isInitialized; }
        }

        public long Next()
        {
            lock (_obj)//2011-03-04 虽然_obj不是一个静态的对象，不过如果不加这个，的确会有并发问题存在
            {
                if (_left == 0)
                {
                    Next(_bufferSize);
                    _left = _bufferSize;
                }

                _left--;//剩下的减一

                //return _nowId - _left;//_nowId已经加上了BUFFER_SIZE
                return _nowId - _left * _increment;//_nowId已经加上了BUFFER_SIZE 2011-02-23 增加 增量
            }

            //方案2
            //lock (_obj)
            //{
            //    if (_queue.Count == 0)
            //    {
            //        List<int> list = Next(BUFFER_SIZE);

            //        foreach (int i in list)
            //            _queue.Enqueue(i);

            //        list.Clear();
            //    }

            //    return _queue.Dequeue();
            //}

            //方案一
            //return Next(1)[0];
        }

        #region 方案3

        /// <summary>
        /// 使用该方法，一次批量申请很多id号，私有方法，该方法非线程安全的，来设置_nowId的，同时写入到文件
        /// </summary>
        /// <param key="count"></param>
        private void Next(int count)
        {
            Init();//确保已经初始化

            //lock (_obj)//取消lock，因为在取的时候，已经有一个lock了
            //{

            _nowId += count * _increment;//2011-02-23增加增量 //_nowId += count;//重新设置_nowId
            //Console.WriteLine(_increment);

            //写入文件，每次都要写入一次，免得出了意外情况
            _dataStore.Set(_key, _nowId);//Tools.IO.WriteStringToFile(_nowId.ToString(), _filePath, Encoding.UTF8);

            //Console.WriteLine("写库了");
            //}
        }

        #endregion

        #region 方案1-2 淘汰

        /*/// <summary>
        /// 建议使用该方法，一次批量申请很多id号
        /// </summary>
        /// <param key="count"></param>
        /// <returns></returns>
        private List<int> Next(int count)
        {
            Init();//确保已经初始化

            List<int> list = new List<int>();

            lock (_obj)
            {
                for (int i = 1; i <= count; i++)
                    list.Add(_nowId + i);

                _nowId += count;//重新设置_nowId

                //写入文件，每次都要写入一次，免得出了意外情况
                _dataStore.Set(_key, _nowId);
            }

            return list;
        }*/

        #endregion
    }
}
